#pragma once
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <unordered_map>
#include <mutex>
#include "util.hpp"
#include "log.hpp"

#define X 5
#define Y 1

namespace ns_index
{
    struct DocInfo
    {
        std::string title;  //文档的标题
        std::string content;//文档对应是去标签之后的内容
        std::string url;    //官网文档url
        u_int64_t doc_id;         //文档id
    };
    
    struct InvertedElem
    {
        u_int64_t doc_id;
        std::string word; //关键字
        int weight;       //权重
    };

    typedef std::vector<InvertedElem> InvertedList;

    class Index
    {
        private:
            //正排索引的数据结构用数组，数组的下标天然是文档id
            std::vector<DocInfo> forward_index;//正排索引
            //倒排索引一定是一个关键字和一组/个InvertedElem对应【关键字和倒排拉链的映射关系】
            std::unordered_map<std::string, InvertedList> Inverted_index; //倒排索引
        private:
            Index(){};
            Index(const Index&) = delete;
            Index& operator=(const Index&) = delete;

            static Index* instance;
            static std::mutex mtx;
        public:
            ~Index()
            {

            }
        public:
            static Index* GetInstance()
            {
                if(nullptr == instance)
                {
                    mtx.lock();
                    if(instance == nullptr)
                    {
                        instance = new Index();
                    }
                    mtx.unlock();
                }
                return instance;
            }
            //通过doc_id获取文档内容
            DocInfo *GetForwardIndex(u_int64_t doc_id)
            {
                if(doc_id >= forward_index.size())
                {
                    std::cerr << "doc_id out of range" << std::endl;
                    return nullptr;
                }
                return &forward_index[doc_id];
            }
            //通过关键字string,获取倒排拉链
            InvertedList *GetInvertedList(const std::string &word)
            {
                auto it = Inverted_index.find(word);
                if(it == Inverted_index.end())
                {
                    std::cerr << word << "not found in inverted index" << std::endl;
                    return nullptr;
                }
                return &(it->second);
            }
            //根据去标签，格式化之后的文档，构建正排和倒排索引
            //data/raw_html/raw.txt
            bool BuildIndex(const std::string &input)//parse解析之后的数据交到这里
            {
                std::ifstream in(input,std::ios::binary);
                if(!in.is_open())
                {
                    std::cerr << "sorry" << input << "open error" << std::endl;
                    return false;
                }

                std::string line;
                int count = 0;
                while(std::getline(in, line))
                {
                    DocInfo* doc = BuildForwardIndex(line);
                    if(doc == nullptr)
                    {
                        std::cerr << "build " << line <<"error" << std::endl;
                        continue;
                    }

                    BuildInvertedIndex(*doc);
                    count++;
                    if(count %50 == 0)
                    {
                        LOG(NORAML,"当前的已经建立的索引文档" + std::to_string(count));
                    }
                }
                return true;
            }
        private:
            DocInfo *BuildForwardIndex(const std::string &line)
            {
                //解析Line，字符串切分
                std::string sep = "\3";
                std::vector<std::string> results;
                ns_util::StringUtil::Split(line, &results, sep);
                if(results.size() != 3)
                {
                    return nullptr;
                }

                //字符串进行填充到DocInfo
                DocInfo doc;
                doc.title = results[0];
                doc.content = results[1];
                doc.url = results[2];
                doc.doc_id = forward_index.size();
                //插入到正排索引的vector中
                forward_index.push_back(std::move(doc));
                return &forward_index.back();
            }
            bool BuildInvertedIndex(const DocInfo &doc)
            {
                struct word_cnt
                {
                    int title_cnt;
                    int content_cnt;
                    word_cnt():title_cnt(0), content_cnt(0)
                    {}
                };
                std::unordered_map<std::string, word_cnt> word_map;//用来暂存词频的映射表
                //标题词频统计
                std::vector<std::string> title_words;
                ns_util::JiebaUtil::CutString(doc.title, &title_words);

                

                for(auto &s : title_words)
                {
                    word_map[s].title_cnt++;
                }
                //文档内容词频统计
                std::vector<std::string> content_words;
                ns_util::JiebaUtil::CutString(doc.content, &content_words);
                for(auto &s : content_words)
                {
                    word_map[s].content_cnt++;
                }
                //构建倒排索引
                for(auto &word_pair : word_map)
                {
                    InvertedElem item;
                    item.doc_id = doc.doc_id;
                    item.word = word_pair.first;
                    item.weight = X*word_pair.second.title_cnt + Y*word_pair.second.content_cnt;
                    InvertedList &inverted_list = Inverted_index[word_pair.first];
                    inverted_list.push_back(item);
                }
                return true;
            }    
    };
    Index* Index::instance = nullptr;
    std::mutex Index::mtx;
}